#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2011

# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation

# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Author: Jesus Carrero <j.o.carrero@gmail.com>
# Mountain View, CA
#

from __future__ import division

from models.AnalyticalFormulas import vanillaCallPrice, \
    vanillaPutPrice
from numpy import r_, asarray
from scipy.optimize import fmin

import unittest


class OptTestSuite(unittest.TestCase):

    def setUp(self):

        (self.m_iRate, self.m_strikes) = (0.05, [30, 40, 50])
        (self.m_prices, self.m_sigmas, self.m_T) = ([[20], [60], [60]], \
                [0.3, 0.31, 0.4], [0.2, 0.4, 0.5])

        callPrices = asarray([vanillaCallPrice(S0, K, self.m_iRate, sg,
                             tm) for (S0, K, sg, tm) in
                             zip(self.m_prices, self.m_strikes,
                             self.m_sigmas, self.m_T)])

        putPrices = asarray([vanillaPutPrice(S0, K, self.m_iRate, sg, tm)
                            for (S0, K, sg, tm) in zip(self.m_prices,
                            self.m_strikes, self.m_sigmas, self.m_T)])

        self.m_exactSol = r_[callPrices.flatten(), putPrices.flatten()]

    def _oneStep(self, prices, strikes, iRate, sig, T):

        callPrices = asarray([vanillaCallPrice(S0, K, iRate, sig, tm)
                             for (S0, K, tm) in zip(prices, strikes,
                             T)])

        putPrices = asarray([vanillaPutPrice(S0, K, iRate, sig, tm)
                            for (S0, K, tm) in zip(prices, strikes, T)])

        return r_[callPrices.flatten(), putPrices.flatten()]


class CalVolAnalytical(OptTestSuite):

    def costFunction(self, sig):
        (iRate, strikes, prices, T) = (self.m_iRate, self.m_strikes, \
                self.m_prices, self.m_T)

        optSol = self._oneStep(prices, strikes, iRate, sig, T)
        return sum((self.m_exactSol - optSol) ** 2)

    def runTest(self):
        sig0 = [0.6]

        print ''
        xopt = fmin(self.costFunction, sig0, xtol=1e-8)
        assert abs(xopt - 0.39622424) < 1.0e-6

def mySuite():

    suite = unittest.TestSuite()
    suite.addTest(CalVolAnalytical())
    return suite


if __name__ == '__main__':

    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(mySuite())

